package com.selebash.ast;

import com.selebash.exception.SelebashException;
import com.selebash.features.FeatureHolder;
import com.selebash.runtime.ExecutionContext;
import com.selebash.runtime.Parseable;
import com.selebash.utils.AnyToAnyConverter;
import java.lang.reflect.InvocationTargetException;
import java.util.regex.Matcher;
import org.apache.commons.lang3.StringUtils;

/**
 * @author sad
 */
public class FeatureAst extends SequenceAst {

    private String line;

    public String getLine() {
        return line;
    }

    public void setLine(String line) {
        this.line = line;
    }

    private boolean isChild(Class child, Class parent) {
        try {
            child.asSubclass(parent);
            return true;
        } catch (ClassCastException ex) {
        }
        return false;
    }

    private Object[] formatArguments(ExecutionContext executionContext, FeatureHolder fh) {
        Class[] params = fh.getMethod().getParameterTypes();
        Object[] args = new Object[params.length];
        Matcher matcher = fh.getPattern().matcher(line);
        matcher.matches();
        int groupIndex = 1;
        for (int i = 0; i < params.length; i++) {
            Class c = params[i];
            if (c == String.class || c == Integer.class || c == Float.class || c == Double.class || c == Boolean.class || c == int.class || c == float.class || c == double.class || c == boolean.class) {
                if (groupIndex >= (matcher.groupCount() + 1)) {
                    if (c == int.class) {
                        args[i] = 0;
                    } else if (c == float.class) {
                        args[i] = 0.0f;
                    } else if (c == double.class) {
                        args[i] = 0.0;
                    } else if (c == boolean.class) {
                        args[i] = false;
                    } else {
                        args[i] = null;
                    }
                } else {
                    String value = matcher.group(groupIndex);
                    value = StringUtils.strip(value);
                    Object obj = AnyToAnyConverter.convert(String.class, c, value);
                    args[i] = obj;
                    groupIndex++;
                }
            } else if (c == ExecutionContext.class) {
                args[i] = executionContext;
            } else if (isChild(c, AbstractAst.class)) {
                args[i] = this;
            } else if (isChild(c, Parseable.class)) {
                Object result = null;
                if (groupIndex < (matcher.groupCount() + 1)) {
                    result = createParseableInstance(c, matcher.group(groupIndex));
                }
                args[i] = result;
                groupIndex++;
            }
        }
        return args;
    }

    private Object createParseableInstance(Class clazz, String value) {
        try {
            Object obj = clazz.newInstance();
            ((Parseable) obj).parse(StringUtils.strip(value));
            return obj;
        } catch (InstantiationException | IllegalAccessException ex) {
            throw new SelebashException("Cannot instantiate " + clazz.getName(), ex);
        }
    }

    @Override
    public void process(ExecutionContext executionContext) {
        try {
            Object[] args = formatArguments(executionContext, featureHolder);
            Object result=featureHolder.getMethod().invoke(featureHolder.getObjectFeatureOwner(), args);
            executionContext.setLastResult(result);
            if (executionContext.isRunNested()) {
                processNested(executionContext);
            }
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
            throw new SelebashException("Error while executing feature", ex);
        }
    }

    public void processNested(ExecutionContext executionContext) {
        super.runSequenceElements(executionContext, executionContext.getNestedExecutionSettings());
    }

    @Override
    public String toString() {
        return featureHolder.toString();
    }

}
